package com.springboot.template.service.impl;

import java.util.Date;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.springboot.template.domain.SecurityUser;
import com.springboot.template.service.ISecurityUserService;
import com.springboot.template.utils.DateUtil;
import com.springboot.template.domain.request.SecurityKeyInfoAddReq;
import com.springboot.template.domain.request.SecurityKeyInfoFetchReq;
import com.springboot.template.utils.CryptoUtils;
import com.springboot.template.utils.RSAUtil;
import com.springboot.template.utils.redis.RedisCache;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springboot.template.mapper.SecurityKeyInfoMapper;
import com.springboot.template.domain.SecurityKeyInfo;
import com.springboot.template.service.ISecurityKeyInfoService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;


/**
 * 安全密钥信息Service业务层处理
 * 
 * @author Zhang Yu
 * @date 2024-05-28
 */
@Service
public class SecurityKeyInfoServiceImpl implements ISecurityKeyInfoService 
{
    @Autowired
    private SecurityKeyInfoMapper securityKeyInfoMapper;

    //此处为增加的关键语句
    @Autowired
    private ISecurityKeyInfoService securityKeyInfoService ;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ISecurityUserService securityUserService;

    /**
     * 查询安全密钥信息
     * 
     * @param id 安全密钥信息主键
     * @return 安全密钥信息
     */
    @Override
    public SecurityKeyInfo selectSecurityKeyInfoById(Integer id)
    {
        return securityKeyInfoMapper.selectSecurityKeyInfoById(id);
    }

    /**
     * 查询安全密钥信息列表
     * 
     * @param securityKeyInfo 安全密钥信息
     * @return 安全密钥信息
     */
    @Override
    public List<SecurityKeyInfo> selectSecurityKeyInfoList(SecurityKeyInfo securityKeyInfo)
    {
        return securityKeyInfoMapper.selectSecurityKeyInfoList(securityKeyInfo);
    }

    @Override
    @Cacheable(value = {"securityKey"}, key="'channelId:' + #securityKeyInfo.channelId+':type:' + #securityKeyInfo.type", unless="#result==null")
    public SecurityKeyInfo selectSecurityKeyInfoByInfo(SecurityKeyInfo securityKeyInfo) {
        List<SecurityKeyInfo> securityKeyInfos = securityKeyInfoMapper.selectSecurityKeyInfoList(securityKeyInfo);
        if (securityKeyInfos.size() == 0) {
            return null;
        }else {
            return securityKeyInfos.get(0);
        }
    }

    /**
     * 新增安全密钥信息
     * 
     * @param securityKeyInfo 安全密钥信息
     * @return 结果
     */
    @Override
    public int insertSecurityKeyInfo(SecurityKeyInfo securityKeyInfo)
    {
        return securityKeyInfoMapper.insertSecurityKeyInfo(securityKeyInfo);
    }

    @Override
    public int uploadClientRSAPublicKey(SecurityKeyInfoAddReq securityKeyInfoAddReq) {
        int result = 0;
        //1.1.需要判断客户端传入的过期时间是否在当前时间之后,如果不在之后需要提示用户
        Date nowDate = new Date(System.currentTimeMillis());
        if(!DateUtil.isLater(nowDate, securityKeyInfoAddReq.getExpireTime())) {
            System.out.println("过期时间应该在当前时间之后");
        }
        //1.通过客户端的channelId,typeId,查询redis中|数据库中(redis中无数据则查数据)是否存在,是否过期
        SecurityKeyInfo securityKeyInfo = new SecurityKeyInfo();
        securityKeyInfo.setChannelId(securityKeyInfoAddReq.getChannelId());
        securityKeyInfo.setType(1);
        SecurityKeyInfo securityKeyInfo1 = securityKeyInfoService.selectSecurityKeyInfoByInfo(securityKeyInfo);
        if (securityKeyInfo1 != null) {
            result = 1;
            //1.2.存在,比对密钥值是否一致,一致则只更新过期时间(如果新日期在老日期之后),不一致则额外更新密钥值,存入数据库和redis
            System.out.println("公钥为:" + securityKeyInfo1.getContent());
            if (securityKeyInfoAddReq.getContent().equals(securityKeyInfo1.getContent())) { //密钥一致
                if (DateUtil.isLater(securityKeyInfo1.getExpireTime(), securityKeyInfoAddReq.getExpireTime())) {//新日期在老日期之后
                    securityKeyInfo1.setExpireTime(securityKeyInfoAddReq.getExpireTime());
                    result = securityKeyInfoMapper.updateSecurityKeyInfo(securityKeyInfo1);
                    //删除redis缓存数据
                    redisCache.redisTemplate.delete("securityKey::channelId:" + securityKeyInfo1.getChannelId() + ":type:" + securityKeyInfo1.getType());
                }
            }else {//密钥不一致
                securityKeyInfo1.setContent(removeHeaderAndFooter(securityKeyInfoAddReq.getContent()));
                securityKeyInfo1.setExpireTime(securityKeyInfoAddReq.getExpireTime());
                result = securityKeyInfoMapper.updateSecurityKeyInfo(securityKeyInfo1);
                //删除redis缓存数据
                redisCache.redisTemplate.delete("securityKey::channelId:" + securityKeyInfo1.getChannelId() + ":type:" + securityKeyInfo1.getType());
            }
        }else {
            //1.3.不存在,直接入库并存入redis缓存
            securityKeyInfo.setContent(removeHeaderAndFooter(securityKeyInfoAddReq.getContent()));
            securityKeyInfo.setExpireTime(securityKeyInfoAddReq.getExpireTime());
            securityKeyInfo.setRemark("客户端RSA公钥-"+securityKeyInfo.getChannelId());
            result = insertSecurityKeyInfo(securityKeyInfo);
        }
        //返回处理结果
        return result;
    }

    /**
     * 去除公钥首位行内容
     * @param pemContent
     * @return
     */
    private String removeHeaderAndFooter(String pemContent) {
        String NEW_LINE_CHARACTER = "\n";
        String PUBLIC_KEY_START_KEY_STRING = "-----BEGIN PUBLIC KEY-----";
        String PUBLIC_KEY_END_KEY_STRING = "-----END PUBLIC KEY-----";
        String NEW_CR_CHARACTER = "\r";
        String EMPTY_STRING = "";
        pemContent = pemContent.replaceAll(NEW_LINE_CHARACTER, EMPTY_STRING)
                .replaceAll(PUBLIC_KEY_START_KEY_STRING, EMPTY_STRING)
                .replaceAll(PUBLIC_KEY_END_KEY_STRING, EMPTY_STRING)
                .replaceAll(NEW_CR_CHARACTER, EMPTY_STRING);
        return pemContent;
    }

    @Override
    @Transactional
    public String selectSecurityKey(SecurityKeyInfoFetchReq securityKeyInfoFetchReq) throws Exception {
        String key = "";
        SecurityKeyInfo securityKeyInfo = new SecurityKeyInfo();
        securityKeyInfo.setChannelId("-1");
        Date expireDate = new Date("2024/12/12 12:12:00");

        //1.如果是获取服务端的RSA公钥
        if (securityKeyInfoFetchReq.getType().equals(1)) {
            //1.1.判断redis中|数据库中是否有已经生成的密钥信息
            securityKeyInfo.setType(securityKeyInfoFetchReq.getType());
            SecurityKeyInfo securityKeyInfoSelect = securityKeyInfoService.selectSecurityKeyInfoByInfo(securityKeyInfo);
            if (securityKeyInfoSelect != null) {
                //1.1.1.如果有返回服务端的RSA公钥
                key = securityKeyInfoSelect.getContent();
            }else {
                //1.1.2.如果没有,生成后存入数据库
                //1.1.2.1.生成公私钥
                Map<String, String> keyMap = RSAUtil.generateKey();
                String publicKeyStr = keyMap.get("publicKeyStr");
                String privateKeyStr = keyMap.get("privateKeyStr");
                //1.1.2.2.入库公钥
                SecurityKeyInfo securityRSAPublicKeyInfo = SecurityKeyInfo.builder()
                        .type(1)
                        .channelId("-1")
                        .content(publicKeyStr)
                        .expireTime(expireDate)
                        .remark("服务端RSA公钥")
                        .build();
                int result = securityKeyInfoMapper.insertSecurityKeyInfo(securityRSAPublicKeyInfo);
                //1.1.2.2.入库私钥
                SecurityKeyInfo securityRSAPrivateKeyInfo = SecurityKeyInfo.builder()
                        .type(2)
                        .channelId("-1")
                        .content(privateKeyStr)
                        .expireTime(expireDate)
                        .remark("服务端RSA私钥")
                        .build();
                int result1 = securityKeyInfoMapper.insertSecurityKeyInfo(securityRSAPrivateKeyInfo);
                if (result1 > 0 && result > 0) {
                    key = publicKeyStr;
                }
            }
        }else if (securityKeyInfoFetchReq.getType().equals(3)) {
            //2.如果是获取AES密钥
            //2.1.1.判断redis中|数据库中是否有已经生成的密钥信息
            securityKeyInfo.setType(securityKeyInfoFetchReq.getType());
            securityKeyInfo.setChannelId(securityKeyInfoFetchReq.getChannelId());
            SecurityKeyInfo securityAESKeyInfoSelect = securityKeyInfoService.selectSecurityKeyInfoByInfo(securityKeyInfo);
            if (securityAESKeyInfoSelect != null) {
                //2.1.1.1.如果有则通过客户端RSA公钥加密AES密钥,返回AES加密后的密钥信息
                //获取对应的客户端RSA公钥
                key = fetchEncryptAESKey(securityKeyInfoFetchReq.getChannelId(), securityAESKeyInfoSelect.getContent());
            }else {
                //2.1.2.1.如果没有,则生成AES密钥,入库和Redis中,通过客户端RSA公钥加密AES密钥,返回AES加密后的密钥信息
                String aesKey = CryptoUtils.generateSymmetricKey(CryptoUtils.Algorithm.Encryption.AES_ECB_PKCS5);
                SecurityKeyInfo securityAESKeyInfoInsert = SecurityKeyInfo.builder()
                        .type(3)
                        .channelId(securityKeyInfoFetchReq.getChannelId())
                        .expireTime(expireDate)
                        .remark("AES密钥--"+securityKeyInfoFetchReq.getChannelId())
                        .content(aesKey)
                        .build();
                int i = securityKeyInfoMapper.insertSecurityKeyInfo(securityAESKeyInfoInsert);
                if (i > 0) {
                    //获取对应的客户端RSA公钥
                    key = fetchEncryptAESKey(securityKeyInfoFetchReq.getChannelId(), securityAESKeyInfoInsert.getContent());
                }
            }

        }
        return key;
    }

    /**
     * 生成加密后的AES密钥
     * @param channelId 渠道ID
     * @param aesKey aes key
     * @return 加密后的aes key
     * @throws Exception
     */
    private String fetchEncryptAESKey(String channelId, String aesKey) throws Exception {
        String key = "";
        //获取对应channel的RSA公钥
        SecurityKeyInfo securityClientPubKeyInfoQuery = SecurityKeyInfo.builder()
                .type(1)
                .channelId(channelId)
                .build();
        SecurityKeyInfo securityClientPubKeyInfoSelect = securityKeyInfoService.selectSecurityKeyInfoByInfo(securityClientPubKeyInfoQuery);
        if (securityClientPubKeyInfoSelect != null) {
            //加密后的AES key
            key = RSAUtil.encryptByPublicKey(aesKey, securityClientPubKeyInfoSelect.getContent());
        }else {
            System.out.println("该渠道的RSA公钥为空, 请提前上传!");
        }
        return key;
    }

    /**
     * 修改安全密钥信息
     * 
     * @param securityKeyInfo 安全密钥信息
     * @return 结果
     */
    @Override
    public int updateSecurityKeyInfo(SecurityKeyInfo securityKeyInfo)
    {
        return securityKeyInfoMapper.updateSecurityKeyInfo(securityKeyInfo);
    }

    /**
     * 批量删除安全密钥信息
     * 
     * @param ids 需要删除的安全密钥信息主键
     * @return 结果
     */
    @Override
    public int deleteSecurityKeyInfoByIds(Integer[] ids)
    {
        return securityKeyInfoMapper.deleteSecurityKeyInfoByIds(ids);
    }

    /**
     * 删除安全密钥信息信息
     * 
     * @param id 安全密钥信息主键
     * @return 结果
     */
    @Override
    public int deleteSecurityKeyInfoById(Integer id)
    {
        return securityKeyInfoMapper.deleteSecurityKeyInfoById(id);
    }

    @Override
    public String testRequest(String encryptString, String channelId) throws Exception {
        String resultString = "";
        JSONObject jsonObject = new JSONObject();
        String aesKey = fetchAESKey(channelId);
        if (StringUtils.isNotEmpty(aesKey)) {
            //解密密文
            String decryptString = CryptoUtils.decryptSymmetrically(aesKey, null, encryptString, CryptoUtils.Algorithm.Encryption.AES_ECB_PKCS5);
            System.out.println("decryptString = " + decryptString);
            JSONObject jsonObject1 = JSON.parseObject(decryptString);
            //判断如果包含token,校验token
            if (jsonObject1.containsKey("token")) {
                String token = jsonObject1.getString("token");
                String channel = jsonObject1.getString("channel");
                Long userId = jsonObject1.getLong("userId");
                SecurityUser securityUser = SecurityUser.builder().token(token).channel(channel).userId(userId).build();
                int result = securityUserService.verifySecurityUser(securityUser);
                if (result > 0) {

                }else {
                    throw new Exception("token校验失败");
                }
            }
            jsonObject.put("data", jsonObject1);
            String responseString = JSON.toJSONString(jsonObject1);
            String encryptText = CryptoUtils.encryptSymmetrically(aesKey, null, responseString, CryptoUtils.Algorithm.Encryption.AES_ECB_PKCS5);
            resultString = encryptText;
        }
        return resultString;
    }

    /**
     * 根据channelId获取AES密钥
     * @param channelId
     * @return
     */
    private String fetchAESKey(String channelId) {
        SecurityKeyInfo aesKeyInfoQuery = SecurityKeyInfo.builder().channelId(channelId).type(3).build();
        List<SecurityKeyInfo> securityAESKeyInfoList = securityKeyInfoMapper.selectSecurityKeyInfoList(aesKeyInfoQuery);
        SecurityKeyInfo securityKeyInfo = securityAESKeyInfoList.stream().findFirst().orElse(new SecurityKeyInfo());
        return securityKeyInfo.getContent();
    }


}
